package org.insist.best.codeGenerator.core;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import cn.hutool.setting.dialect.Props;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.insist.best.codeGenerator.vo.ColumnListItem;
import org.insist.best.codeGenerator.vo.TableListItem;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class GenerateUtil {

    private static Props dataTypeMap = new Props("typeMapping.properties");

    private static TemplateEngine templateEngine =
            TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));

    public static void generateCode(TableListItem tableListItem,
                                    List<ColumnListItem> columnListItems,
                                    ZipOutputStream zip, GenerateConfig generateConfig) {
        // 表
        TableEntity tableEntity = toTableEntity(tableListItem, generateConfig.getTablePrefix());
        Boolean hasBigDecimal = false;
        Boolean hasList = false;
        Boolean hasLocalDateTime = false;
        fillTableEntity(columnListItems, tableEntity, hasBigDecimal, hasList, hasLocalDateTime);
        Map<String, Object> templateParams = buildTemplateParams(tableEntity, generateConfig, hasBigDecimal, hasList, hasLocalDateTime);
        List<String> templates = getTemplates();

        for (String template : templates) {
            Template tpl = templateEngine.getTemplate(template);
            try {
                zip.putNextEntry(new ZipEntry(getFileName(template,
                        tableEntity.getClassNameFirstUpperCase(),
                        generateConfig.getPackageName(),
                        generateConfig.getModuleName())));
                IoUtil.write(zip, "UTF-8", false, tpl.render(templateParams));
                zip.closeEntry();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
    /**
     * 获取文件名
     */
    public static String getFileName(String template, String className, String packageName, String moduleName) {
        String packagePath = "main" + File.separator + "java" + File.separator;
        if (StringUtils.isNotBlank(packageName)) {
            packagePath += moduleName +  File.separator + packageName.replace(".", File.separator) + File.separator;
        }
        if (template.contains("Entity.java.ftl")) {
            return packagePath + "entity" + File.separator + className + "Entity.java";
        }

        if (template.contains("Dao.java.ftl")) {
            return packagePath + "dao" + File.separator + className + "Dao.java";
        }

        if (template.contains("Service.java.ftl")) {
            return packagePath + "service" + File.separator + className + "Service.java";
        }

        if (template.contains("ServiceImpl.java.ftl")) {
            return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
        }

        if (template.contains("Controller.java.ftl")) {
            return packagePath + "controller" + File.separator + className + "Controller.java";
        }

        if (template.contains("Dao.xml.ftl")) {
            return "main" + File.separator + "resources" + File.separator + "mapper" + File.separator + moduleName + File.separator + className + "Dao.xml";
        }
        return null;
    }


    private static List<String> getTemplates() {
        List<String> templates = new ArrayList<>();
        templates.add("Entity.java.ftl");
        templates.add("Dao.java.ftl");
        templates.add("Service.java.ftl");
        templates.add("ServiceImpl.java.ftl");
        templates.add("Controller.java.ftl");
        return templates;
    }

    private static Map<String, Object> buildTemplateParams(TableEntity tableEntity,
                                                           GenerateConfig generateConfig,
                                                           Boolean hasBigDecimal,
                                                           Boolean hasList, Boolean hasLocalDateTime) {
        Map<String, Object> templateParams = new HashMap<>();
        templateParams.put("tableName", tableEntity.getTableName());
        templateParams.put("comments", tableEntity.getComments());
        templateParams.put("pk", tableEntity.getPk());
        templateParams.put("classNameFirstUpperCase", tableEntity.getClassNameFirstUpperCase());
        templateParams.put("classNameFirstLowerCase", tableEntity.getClassNameFirstLowerCase());
        templateParams.put("columns", tableEntity.getColumns());
        templateParams.put("hasBigDecimal", tableEntity.getHasBigDecimal());
        templateParams.put("hasList", tableEntity.getHasList());
        templateParams.put("package", generateConfig.getPackageName());
        templateParams.put("moduleName", generateConfig.getModuleName());
        templateParams.put("author", generateConfig.getAuthor());
        templateParams.put("datetime", DateUtil.formatDate(new Date()));
        templateParams.put("hasLocalDateTime", tableEntity.getHasLocalDateTime());
        return templateParams;
    }

    private static void  fillTableEntity(List<ColumnListItem> columnListItems,
                                                       TableEntity tableEntity,
                                                       Boolean hasBigDecimal,
                                                       Boolean hasList, Boolean hasLocalDatetime) {
        List<ColumnEntity> columnEntities = new ArrayList<>();
        for (ColumnListItem columnListItem : columnListItems) {
            ColumnEntity columnEntity = new ColumnEntity();
            columnEntity.setColumnName(columnListItem.getColumnName());
            columnEntity.setDataType(columnListItem.getDateType());
            columnEntity.setComments(columnListItem.getColumnComment());
            columnEntity.setExtra(columnListItem.getExtra());

            String attrName = columnToJava(columnListItem.getColumnName());
            columnEntity.setAttrNameFirstUpperCase(attrName);
            columnEntity.setAttNameFirstLowCase(StrUtil.lowerFirst(attrName));
            String attrType = dataTypeMap.getStr(columnEntity.getDataType(), columnToJava(columnEntity.getDataType()));
            columnEntity.setAttrType(attrType);

            if (!hasBigDecimal && attrType.equals("BigDecimal")) {
                hasBigDecimal = true;
            }
            if (!hasList && "array".equals(columnEntity.getExtra())) {
                hasList = true;
            }
            if (!hasLocalDatetime && attrType.equals("LocalDateTime")) {
                hasLocalDatetime = true;
            }
            // 是否主键
            if ("PRI".equalsIgnoreCase(columnListItem.getColumnKey()) && tableEntity.getPk() == null) {
                tableEntity.setPk(columnEntity);
            }
            columnEntities.add(columnEntity);
        }
        tableEntity.setColumns(columnEntities);
        if (tableEntity.getPk() == null) {
            tableEntity.setPk(tableEntity.getColumns().get(0));
        }
        tableEntity.setHasBigDecimal(hasBigDecimal);
        tableEntity.setHasList(hasList);
        tableEntity.setHasLocalDateTime(hasLocalDatetime);
    }

    private static TableEntity toTableEntity(TableListItem tableListItem, String tablePrefix) {
        TableEntity tableEntity = new TableEntity();
        tableEntity.setTableName(tableListItem.getTableName());
        tableEntity.setComments(tableListItem.getTableComment());
        // java 类名
        String className = tableToJava(tableEntity.getTableName(), tablePrefix);
        tableEntity.setClassNameFirstUpperCase(className);
        tableEntity.setClassNameFirstLowerCase(StrUtil.lowerFirst(className));
        return tableEntity;
    }

    private static String tableToJava(String tableName, String tablePrefix) {
        if (tableName.startsWith(tablePrefix)) {
            tableName = tableName.replaceFirst(tablePrefix, "");
        }

        return columnToJava(tableName);
    }

    private static String columnToJava(String columnName) {
        return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
    }
}
